package com.cyf.xjtech_android

import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
import android.graphics.RectF
import android.util.Log
import com.cyf.xjtech_android.bean.TmpData
import com.sun.jna.Pointer
import com.sun.jna.Structure
import java.net.NetworkInterface
import java.net.SocketException
import java.util.*
import kotlin.collections.ArrayList

object XJTechSDK {
    private const val TAG = "XJTechSDK"
    var imageWidth = 384
    var imageHeight = 288
    private var userHandle = -1
    private val cacheData = CustomQueue<TmpData>()
    private var isArmpitEnable = false

    private val callback = object : XJTechLibrary.PUPLOAD_DATA_CALLBACK {
        override fun callback(
            pImage: Pointer?,
            pMaxTemprInfo: X_TEMPR_INFO?,
            pMinTemprInfo: X_TEMPR_INFO?,
            pAlarmLinkedArea: X_ALARM_LINDED_AREA?,
            iAlarmAreaCount: Int,
            pAlarmMask: Pointer?,
            pUserData: Pointer?
        ) {
            if (pImage == null) {
                Log.e("MainActivity", "pImage data is null")
                return
            }
            val length = imageWidth * imageHeight * 3
            val bytes = pImage.getByteArray(0, length)
            val tmpData = TmpData(imageWidth, imageHeight)
            tmpData.bmp = bytes
            if (pMaxTemprInfo != null) {
                tmpData.maxX = pMaxTemprInfo.iX
                tmpData.maxY = pMaxTemprInfo.iY
                tmpData.max = pMaxTemprInfo.iTempr.toFloat() / 10
            }
            if (iAlarmAreaCount > 0 && pAlarmLinkedArea != null) {
                tmpData.maxX = pAlarmLinkedArea.pCentroidPt.iX
                tmpData.maxY = pAlarmLinkedArea.pCentroidPt.iY
                tmpData.max = pAlarmLinkedArea.iDstTempr.toFloat() / 10
            }
//            if (iAlarmAreaCount > 0 && pAlarmLinkedArea != null) {
//                val xa = Structure.newInstance(X_ALARM_LINDED_AREA::class.java, pAlarmLinkedArea)
//                tmpData.maxX = xa.pCentroidPt.iX
//                tmpData.maxY = xa.pCentroidPt.iY
//                tmpData.max = xa.iDstTempr.toFloat() / 10
//            }
            if (pMinTemprInfo != null) {
                tmpData.minX = pMinTemprInfo.iX
                tmpData.minY = pMinTemprInfo.iY
                tmpData.min = pMinTemprInfo.iTempr.toFloat() / 10
            }
            Log.d(
                TAG,
                "count=$iAlarmAreaCount   max=${tmpData.max} x=${tmpData.maxX}  y=${tmpData.maxY}"
            )
            if (isArmpitEnable) {
                tmpData.max =
                    XJTechLibrary.INSTANCE.XJTech_SurfaceTemprToArmpitTempr(userHandle, tmpData.max)
            }
            cacheData.push(tmpData)
        }

    }

    /**
     * 打开相机
     */
    fun openCamera(
        ip: String,
        fps: Int = 15,
        w: Int = imageWidth,
        h: Int = imageHeight
    ): Boolean {
        imageWidth = w
        imageHeight = h
        val eDeviceType: X_DEVICE_TYPE = X_DEVICE_TYPE.E_TCP_HISI
        val pDeviceParam = X_DEVICE_PARAM()
        pDeviceParam.setiFrameRate(fps)
        pDeviceParam.setiSrcWidth(imageWidth)
        pDeviceParam.setiSrcHeight(imageHeight)
        pDeviceParam.setiDstWidth(imageWidth)
        pDeviceParam.setiDstHeight(imageHeight)
        pDeviceParam.setiCutHorOffset(0)
        pDeviceParam.setiCutVerOffset(0)
        pDeviceParam.setbCut(1)
        pDeviceParam.seteAlarmType(X_ALARM_TYPE.E_HIGH_ALARM.ordinal)
        pDeviceParam.seteFlipType(X_FLIP_TYPE.E_FLIP_NONE.ordinal)
        userHandle = XJTechLibrary.INSTANCE.XJTech_Init(eDeviceType.ordinal, pDeviceParam)
        val deviceInfo = X_DEVICE_INFO()
        deviceInfo.setSzIpAddr(ip)
        val localAddr = getLocalIpAddress()
        deviceInfo.setSzGateWay(localAddr)
        val rel = XJTechLibrary.INSTANCE.XJTech_Open(userHandle, deviceInfo, callback, null)
        if (rel < 0) {
            Log.e("打开失败", "rel=$rel")
            return false
        }
        return true
    }

    /**
     * 关闭相机
     * 耗时操作
     */
    fun closeCamera(): Boolean {
        if (userHandle < 0) {
            Log.e(TAG, "camera is not open")
            return false
        }
        XJTechLibrary.INSTANCE.XJTech_Close(userHandle)
        XJTechLibrary.INSTANCE.XJTech_UnInit(userHandle)
        userHandle = -1
        return true
    }

    /**
     * 设置黑体校准
     * @param isOpen 是否启用
     * @param fTemper 黑体温度，不传值默认为37℃
     * @param rect 黑体在图像中的位置，默认为左上角
     */
    fun setBdCorrect(
        isOpen: Boolean,
        fTemper: Float = 37f,
        rect: Rect = Rect(2, 3, 4, 5)
    ): Boolean {
        if (userHandle < 0) {
            Log.e(TAG, "camera is not open")
            return false
        }
        if (isOpen) {
            val rel = XJTechLibrary.INSTANCE.XJTech_SetBlackBodyBaseTempr(userHandle, fTemper)
            if (!rel) {
                Log.e(TAG, "base temper set failed")
                return false
            }
            val pos = byteArrayOf(
                rect.left.toByte(),
                0,
                rect.top.toByte(),
                0,
                rect.right.toByte(),
                0,
                rect.bottom.toByte(),
                0
            )
            XJTechLibrary.INSTANCE.XJTech_SetBlackBodyRegion(userHandle, pos, pos.size)
        }
        XJTechLibrary.INSTANCE.XJTech_StartBdCorrectMode(userHandle, isOpen)
        return true
    }

    /**
     * 设置调色板
     */
    fun setPalette(palette: Int): Boolean {
        if (userHandle < 0) {
            Log.e(TAG, "camera is not open")
            return false
        }
        XJTechLibrary.INSTANCE.XJTech_ChangePalette(userHandle, palette)
        return true
    }

    /**
     * 快门矫正
     */
    fun shutterCorrect(): Boolean {
        if (userHandle < 0) {
            Log.e(TAG, "camera is not open")
            return false
        }
        XJTechLibrary.INSTANCE.XJTech_Shutter(userHandle)
        return true
    }

    /**
     * 设置腋下温度是否启用
     */
    fun setArmpitEnable(enable: Boolean): Boolean {
        if (userHandle < 0) {
            Log.e(TAG, "camera is not open")
            return false
        }
        isArmpitEnable = enable
        return true
    }

    /**
     * 设置屏蔽区域
     */
    fun setHiddenArea(w: Int, h: Int, hiddenPos: ArrayList<Int>): Boolean {
        if (userHandle < 0) {
            Log.e(TAG, "camera is not open")
            return false
        }
        XJTechLibrary.INSTANCE.XJTech_SetShieldGridSize(userHandle, w, h)
        val rect = ByteArray(w * h)
        for (i in rect.indices) {
            if (hiddenPos.contains(i)) {
                rect[i] = 1
            } else {
                rect[i] = 0
            }
        }
        XJTechLibrary.INSTANCE.XJTech_SetShieldArea(
            userHandle,
            rect,
            rect.size
        )
        return true
    }

    /**
     * 获取指定点温度
     */
    fun getTemprByPoint(x: Int, y: Int): Float {
        if (userHandle < 0) {
            Log.e(TAG, "camera is not open")
            return 0f
        }
        if (x < 0 || x >= imageWidth || y < 0 || y >= imageHeight) {
            Log.e(TAG, "point is out of image")
            return 0f
        }
        var temper = XJTechLibrary.INSTANCE.XJTech_GetTemprByPoint(userHandle, x, y)
        if (isArmpitEnable) {
            temper = XJTechLibrary.INSTANCE.XJTech_SurfaceTemprToArmpitTempr(userHandle, temper)
        }
        return temper
    }

    /**
     * 设置报警等级区间（包含设置值）
     * @param minStart 低温报警区间左侧
     * @param minEnd 低温报警区间右侧
     * @param maxStart 高温报警区间左侧
     * @param maxEnd 高温报警区间右侧
     */
    public fun setAlarmLevel(minStart: Float, minEnd: Float, maxStart: Float, maxEnd: Float) {
        if (userHandle < 0) {
            Log.e(TAG, "camera is not open")
            return
        }
        if(minStart > minEnd || maxStart > maxEnd){
            Log.e(TAG, "param is invalid")
            return
        }
        val alarm = X_ALARM_LEVEL()
        alarm.fOne = minStart
        alarm.fTwo = minEnd
        alarm.fThree = maxStart
        alarm.fFour = maxEnd
        XJTechLibrary.INSTANCE.XJTech_SetAlarmLevel(userHandle, alarm)
    }

    private val mIR2VIS = Array(3) { DoubleArray(3) }
    private val mVIS2IR = Array(3) { DoubleArray(3) }
    private var transformMatrixInit = false

    /**
     * 初始化变化矩阵
     * 传入红外和可见光图片上相同物体的点位，注意必须是图片坐标，而不是View的坐标
     * @param irPoint 红外图片上的选点
     * @param viPoint 可见光图片上的选点
     */
    fun initTransformMatrix(irPoint: Array<Point>, viPoint: Array<Point>): Boolean {
        val ir = Array(irPoint.size, init = { X_PT() })
        val vi = Array(viPoint.size, init = { X_PT() })
        for (i in ir.indices) {
            ir[i] = X_PT()
            ir[i].iX = irPoint[i].x
            ir[i].iY = irPoint[i].y
        }
        for (i in vi.indices) {
            vi[i] = X_PT()
            vi[i].iX = viPoint[i].x
            vi[i].iY = viPoint[i].y
        }
        var rel = true
        rel = rel && XJTechLibrary.INSTANCE.XJTech_GetTransformMatrix(ir, vi, mIR2VIS)
        rel = rel && XJTechLibrary.INSTANCE.XJTech_GetTransformMatrix(vi, ir, mVIS2IR)
        transformMatrixInit = rel
        return rel
    }

    /**
     * 点变换
     */
    private fun pointTransform(src: PointF, m_TransParameter: Array<DoubleArray>): PointF {
        if (!transformMatrixInit) {
            return src
        }
        val dst = PointF()
        dst.x =
            (m_TransParameter[0][0] * src.x + m_TransParameter[0][1] * src.y + m_TransParameter[0][2]).toFloat()
        dst.y =
            (m_TransParameter[1][0] * src.x + m_TransParameter[1][1] * src.y + m_TransParameter[1][2]).toFloat()
        return dst
    }

    /**
     * 红外转可见光，前置条件，initTransformMatrix进行初始化
     */
    fun ir2VL(irPoint: Point): Point {
        val p = pointTransform(PointF(irPoint.x.toFloat(), irPoint.y.toFloat()), mIR2VIS)
        return Point(p.x.toInt(), p.y.toInt())
    }

    fun ir2VL(irPoint: PointF): PointF {
        return pointTransform(irPoint, mIR2VIS)
    }

    fun ir2VL(irRect: Rect): Rect {
        val ltPoint = ir2VL(Point(irRect.left, irRect.top))
        val rbPoint = ir2VL(Point(irRect.right, irRect.bottom))
        return Rect(ltPoint.x, ltPoint.y, rbPoint.x, rbPoint.y)
    }

    fun ir2VL(irRect: RectF): RectF {
        val ltPoint = ir2VL(PointF(irRect.left, irRect.top))
        val rbPoint = ir2VL(PointF(irRect.right, irRect.bottom))
        return RectF(ltPoint.x, ltPoint.y, rbPoint.x, rbPoint.y)
    }

    /**
     * 可见光转红外，前置条件，initTransformMatrix进行初始化
     */
    fun vl2IR(viPoint: Point): Point {
        val p = pointTransform(PointF(viPoint.x.toFloat(), viPoint.y.toFloat()), mVIS2IR)
        return Point(p.x.toInt(), p.y.toInt())
    }

    fun vl2IR(viPoint: PointF): PointF {
        return pointTransform(viPoint, mVIS2IR)
    }

    fun vl2IR(viRect: Rect): Rect {
        val ltPoint = vl2IR(Point(viRect.left, viRect.top))
        val rbPoint = vl2IR(Point(viRect.right, viRect.bottom))
        return Rect(ltPoint.x, ltPoint.y, rbPoint.x, rbPoint.y)
    }

    fun vl2IR(viRect: RectF): RectF {
        val ltPoint = vl2IR(PointF(viRect.left, viRect.top))
        val rbPoint = vl2IR(PointF(viRect.right, viRect.bottom))
        return RectF(ltPoint.x, ltPoint.y, rbPoint.x, rbPoint.y)
    }

    /**
     * 矩形坐标点变换
     * 将src矩形中的点p，变换成dst矩形中的对应坐标
     * 比如图片中的坐标，转换到View中的坐标进行显示，又或者将View中选中的坐标变换成图片的坐标进行测温
     * @param p 需要变换的点坐标
     * @param src 源矩形
     * @param dst 目标矩形
     * @return 返回变换后的坐标点
     */
    fun transformByRect(p: PointF, src: Rect, dst: Rect): PointF {
        val x = dst.width() * p.x / src.width()
        val y = dst.height() * p.y / src.height()
        return PointF(x, y)
    }

    fun transformByRect(p: Point, src: Rect, dst: Rect): Point {
        val x = dst.width() * p.x / src.width()
        val y = dst.height() * p.y / src.height()
        return Point(x, y)
    }


    /**
     * 获取一帧数据
     */
    fun pullData(): TmpData? {
        return cacheData.pull()
    }

    private fun getLocalIpAddress(): String {
        try {
            val en = NetworkInterface
                .getNetworkInterfaces() ?: return ""
            while (en.hasMoreElements()) {
                val intf = en.nextElement()
                if (intf.name.toLowerCase(Locale.ROOT) == "eth0" || intf.name.toLowerCase(Locale.ROOT) == "wlan0" || intf.name.toLowerCase(
                        Locale.ROOT
                    ) == "ppp0"
                ) {
                    val enumIpAddr = intf.inetAddresses
                    while (enumIpAddr.hasMoreElements()) {
                        val inetAddress = enumIpAddr.nextElement()
                        if (!inetAddress.isLoopbackAddress) {
                            val ipaddress = inetAddress.hostAddress ?: return ""
                            if (!ipaddress.contains("::")) { //ipV6的地址
                                return ipaddress
                            }
                        }
                    }
                }
            }
        } catch (ex: SocketException) {
            Log.e("IpAddress", ex.toString())
        }
        return ""
    }


}